package org.punkgrok.flightrecorder.mock;

import org.punkgrok.flightrecorder.MethodInvocation;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: rfzabick
 * Date: May 23, 2009
 * Time: 4:22:27 PM
 */
public class MockHttpServletRequest implements HttpServletRequest {
	private String queryString;
	private Map<String, String> parameters;
	private String authType;
	private Cookie[] cookies;
	private Map<String, Long> dateHeaders;
	private Map<String, String> headers;
	private Map<String, Integer> intHeaders;
	private String method;
	private String pathInfo;
	private String pathTranslated;
	private String contextPath;
	private String remoteUser;
	private Map<String, Boolean> userInRole;
	private boolean requestedSessionIdValid;
	private boolean requestedSessionIdFromCookie;
	private HttpSession session;
	private Map<String, Object> attributes;
	private String contentType;
	private String characterEncoding;
	private String requestedSessionId;
	private String servletPath;
	private Locale locale;

	public MockHttpServletRequest() {
		parameters = new HashMap<String, String>();
		dateHeaders = new HashMap<String, Long>();
		headers = new HashMap<String, String>();
		intHeaders = new HashMap<String, Integer>();
		userInRole = new HashMap<String, Boolean>();
		attributes = new HashMap<String, Object>();
	}


	public String getAuthType() {
		return authType;
	}

	public void setAuthType(String authType) {
		this.authType = authType;
	}


	public Cookie[] getCookies() {
		return cookies;
	}

	public void setCookies(Cookie[] cookies) {
		this.cookies = cookies;
	}


	public long getDateHeader(String s) {
		return dateHeaders.get(s);
	}

	public void setDateHeader(String s, long value) {
		dateHeaders.put(s, value);
	}


	public String getHeader(String s) {
		return headers.get(s);
	}

	public void setHeader(String s, String value) {
		headers.put(s, value);
	}


	public Enumeration getHeaders(String s) {
		return null;  //TBD
	}

	public Enumeration getHeaderNames() {
		return null;  //TBD
	}


	public int getIntHeader(String s) {
		return intHeaders.get(s);
	}

	public void setIntHeader(String s, int value) {
		intHeaders.put(s, value);
	}


	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}


	public String getPathInfo() {
		return pathInfo;
	}

	public void setPathInfo(String pathInfo) {
		this.pathInfo = pathInfo;
	}


	public String getPathTranslated() {
		return pathTranslated;
	}

	public void setPathTranslated(String pathTranslated) {
		this.pathTranslated = pathTranslated;
	}


	public String getContextPath() {
		return contextPath;
	}

	public void setContextPath(String contextPath) {
		this.contextPath = contextPath;
	}


	public String getQueryString() {
		return queryString;
	}

	public void setQueryString(String queryString) {
		this.queryString = queryString;

		StringTokenizer tok = new StringTokenizer(queryString, "&");
		while (tok.hasMoreTokens()) {
			String nameValuePair = tok.nextToken();
			String name = nameValuePair.substring(0, nameValuePair.indexOf("="));
			String value = nameValuePair.substring(nameValuePair.indexOf("=") + 1);
			parameters.put(name, value);
		}
	}


	public String getRemoteUser() {
		return remoteUser;
	}

	public void setRemoteUser(String remoteUser) {
		this.remoteUser = remoteUser;
	}

	public boolean isUserInRole(String s) {
		return userInRole.get(s);
	}

	public void setUserInRole(String s, boolean value) {
		userInRole.put(s, value);
	}

	public Principal getUserPrincipal() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getRequestedSessionId() {
		return requestedSessionId;
	}

	public void setRequestedSessionId(String requestedSessionId) {
		this.requestedSessionId = requestedSessionId;
	}

	public String getRequestURI() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public StringBuffer getRequestURL() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getServletPath() {
		return servletPath;
	}

	public void setServletPath(String servletPath) {
		this.servletPath = servletPath;
	}

	public HttpSession getSession(boolean b) {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public HttpSession getSession() {
		return session;
	}

	public void setSession(HttpSession session) {
		this.session = session;
	}

	public boolean isRequestedSessionIdValid() {
		return requestedSessionIdValid;
	}

	public void setRequestedSessionIdValid(boolean requestedSessionIdValid) {
		this.requestedSessionIdValid = requestedSessionIdValid;
	}

	public boolean isRequestedSessionIdFromCookie() {
		return requestedSessionIdFromCookie;
	}

	public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) {
		this.requestedSessionIdFromCookie = requestedSessionIdFromCookie;
	}

	public boolean isRequestedSessionIdFromURL() {
		return false;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public boolean isRequestedSessionIdFromUrl() {
		return false;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public Object getAttribute(String s) {
		return attributes.get(s);
	}

	public Enumeration getAttributeNames() {
		return new Hashtable(attributes).keys();
	}

	public String getCharacterEncoding() {
		return characterEncoding;
	}

	public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
		this.characterEncoding = s;
	}

	public int getContentLength() {
		return 0;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

	public ServletInputStream getInputStream() throws IOException {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getParameter(String parameterName) {
		return parameters.get(parameterName);
	}

	public Enumeration getParameterNames() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String[] getParameterValues(String s) {
		return new String[0];  //To change body of implemented methods use File | Settings | File Templates.
	}

	public Map getParameterMap() {
		return Collections.unmodifiableMap(parameters);
	}

	public String getProtocol() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getScheme() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getServerName() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public int getServerPort() {
		return 0;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public BufferedReader getReader() throws IOException {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getRemoteAddr() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getRemoteHost() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public void setAttribute(String s, Object o) {
		attributes.put(s, o);
	}

	public void removeAttribute(String s) {
		attributes.remove(s);
	}

	public Locale getLocale() {
		return locale;
	}

	public void setLocale(Locale locale) {
		this.locale = locale;
	}

	public Enumeration getLocales() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public boolean isSecure() {
		return false;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public RequestDispatcher getRequestDispatcher(String s) {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getRealPath(String s) {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public int getRemotePort() {
		return 0;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getLocalName() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public String getLocalAddr() {
		return null;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public int getLocalPort() {
		return 0;  //To change body of implemented methods use File | Settings | File Templates.
	}

	public static String generateCloneJavaCode(MockHttpServletRequest request) {

		return "MockHttpServletRequest request = new MockHttpServletRequest();\n" +
			"request.setQueryString(\"" + request.getQueryString() + "\");";
	}

	public static String buildReplayRequest(List<MethodInvocation> methodInvocations) {
		String javaCode = "MockHttpServletRequest request = new MockHttpServletRequest();\n";
		for (MethodInvocation methodInvocation : methodInvocations) {
			javaCode += generateCode(methodInvocation);
		}

		return javaCode;
	}

	private static String generateCode(MethodInvocation methodInvocation) {
		String invokedMethodName = methodInvocation.getMethodName();

		String generatedMethodName;
		boolean getter = invokedMethodName.startsWith("get");
		boolean setter = invokedMethodName.startsWith("set");
		boolean booleanGetter = invokedMethodName.startsWith("is");
		if (getter || setter) {
			String propertyName = invokedMethodName.substring(3);
			generatedMethodName = "set" + propertyName;
		} else if (booleanGetter) {
			String propertyName = invokedMethodName.substring(2);
			generatedMethodName = "set" + propertyName;
		} else { //removeAttribute is the only method that doesn't start with set, get or is
			generatedMethodName = "removeAttribute";
		}

		String parameterCode = "";
		if (methodInvocation.getArgs() == null) {
			parameterCode += formatParameter(methodInvocation.getReturnValue());
		} else {
			for (Object arg : methodInvocation.getArgs()) {
				parameterCode += formatParameter(arg);
				parameterCode += ", ";
			}
			if (getter) {
				parameterCode += formatParameter(methodInvocation.getReturnValue());
			} else {
				parameterCode = parameterCode.substring(0, parameterCode.lastIndexOf(", "));
			}
		}

		return "request." + generatedMethodName + "(" + parameterCode + ");\n";
	}

	private static String formatParameter(Object parameter) {
		String tmp = "";
		if (parameter instanceof String) {
			tmp += "\"" + parameter + "\"";
		} else {
			tmp += parameter;
		}
		return tmp;
	}
}
